package com.hmdp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hmdp.dto.Result;
import com.hmdp.entity.Shop;
import com.hmdp.mapper.ShopMapper;
import com.hmdp.service.IShopService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisConstants;
import com.hmdp.utils.RedisData;
import io.netty.util.internal.StringUtil;
import org.apache.tomcat.util.buf.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {

    @Resource
    StringRedisTemplate stringRedisTemplate;

    @Resource
    ShopMapper shopMapper;


    @Override
    public Result queryShopById(Long id) {
        String shopKey = RedisConstants.CACHE_SHOP_KEY + id;
        //1.先查询Redis缓中有没有数据
        String s = stringRedisTemplate.opsForValue().get(shopKey);
        Shop shop = new Shop();
        //Redis不为空返回
        if (StrUtil.isEmpty(s)) {
//            shop = JSONUtil.toBean(s, Shop.class);
            return Result.fail("数据为空！");
        }
        RedisData<Shop> redisData = JSONUtil.toBean(s, RedisData.class);
        Shop date = redisData.getData();
        LocalDateTime expireTime = redisData.getExpireTime();
        //判断是否过期
        if (expireTime.isAfter(LocalDateTime.now().plusSeconds(30))) {
            //未过期，返回旧的店铺信息
            return Result.ok(date);
        }
        //已过期，重建缓存


        //过期，尝试获取互斥锁

        //获取到互斥锁，开启独立线程

        //访问数据库，缓存重建
        //判断命中是否是空值
        if (s == "") {
            Result.fail("数据为空！");
        }

        String lockKey = "lock:shop" + id;
        try {
            //如果为null去获取锁
            if (!getLock(lockKey)) {
                Thread.sleep(50);  //休眠一段时间
                queryShopById(id);  //继续请求
            }
            //2.Redis中没有数据查询数据库中的数据
            shop = shopMapper.selectById(id);
            if (null != shop) {
                //存在写入Redis
                stringRedisTemplate.opsForValue().set(shopKey, JSONUtil.toJsonStr(shop), 30L, TimeUnit.MINUTES);
                return Result.ok(shop);
            }
            //缓存空值，并设置过期时间
            if (null == shop) {
                stringRedisTemplate.opsForValue().set(shopKey, "", 2L, TimeUnit.MINUTES);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            delLock(lockKey);
        }
        return Result.fail("数据不存在！");
    }

    //获取锁
    private Boolean getLock(String key) {
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10L, TimeUnit.MINUTES);
        return BooleanUtil.isTrue(flag);
    }

    //释放锁
    private void delLock(String key) {
        stringRedisTemplate.delete(key);
    }

    //封装一个往数据库添加的方法
    public void saveShop(Long id, Long expireTime) {
        String shopKey = RedisConstants.CACHE_SHOP_KEY + id;
        Shop shop = shopMapper.selectById(id);
        RedisData redisData = new RedisData();
        redisData.setData(shop);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireTime));
        stringRedisTemplate.opsForValue().set(shopKey, JSONUtil.toJsonStr(redisData));
    }

    public void saveShop2Redis(Long id, Long ex) {
        String shopKey = RedisConstants.CACHE_SHOP_KEY + id;
        //2.Redis中没有数据查询数据库中的数据
        Shop shop = shopMapper.selectById(id);
        RedisData<Shop> redisData = new RedisData();
        redisData.setData(shop);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(ex));
        stringRedisTemplate.opsForValue().set(shopKey, JSONUtil.toJsonStr(redisData));

    }


    @Override
    @Transactional
    public Result updateShopById(Shop shop) {
        if (null == shop.getId()) {
            return Result.fail("id不能为空");
        }
        String shopKey = RedisConstants.CACHE_SHOP_KEY + shop.getId();
        //更新数据库
        int i = shopMapper.updateById(shop);
        if (i > 0) {
            //删除缓存
            stringRedisTemplate.delete(shopKey);
        }
        return Result.ok();
    }
}
